// Camera Scene Node
// (c) jimon game studio

#include "CCameraSceneNode.h"

namespace je
{
	namespace scene
	{
		//! Constructor
		CCameraSceneNode::CCameraSceneNode(video::IRender * Render)
			:ICameraSceneNode(Render,"CameraSceneNode"),Options(CameraOptions().Set3D()),Viewport(ViewportOptions()),NeedToRebuildProjMat(true),NeedToRebuildViewMat(true)
		{
			JENEW(Frustum,jeFrustum)
		}

		//! Destructor
		CCameraSceneNode::~CCameraSceneNode()
		{
			Frustum->Drop();
			Frustum = NULL;
		}

		//! Apply Transforms
		void CCameraSceneNode::ApplyTransforms()
		{
			jeVector2di ViewportSize;

			if(Viewport.CurrentFullSizeViewport)
				ViewportSize = CurrentRender->GetContext()->GetSize();
			else
				ViewportSize = Viewport.CurrentSize;

			if(ViewportSize != Viewport.CurrentSize)
			{
				NeedToRebuildProjMat = true;
				NeedToRebuildViewMat = true;
			}

			if(NeedToRebuildProjMat)
			{
				Viewport.CurrentSize = ViewportSize;

				switch(Options.CurrentMode)
				{
				case CM_2D:
					{
						if(Options.CurrentScreenSizeSpace)
						{
							if(Options.CurrentCenterInMiddleOfViewport)
								ProjMat.BuildOrtoMatrix(-(f32)ViewportSize.X/2.0f,(f32)ViewportSize.X/2.0f,-(f32)ViewportSize.Y/2.0f,(f32)ViewportSize.Y/2.0f,Options.CurrentNear,Options.CurrentFar);
							else
								ProjMat.BuildOrtoMatrix(0,ViewportSize.X,0,ViewportSize.Y,Options.CurrentNear,Options.CurrentFar);
						}
						else
							ProjMat.BuildOrtoMatrix(Options.CurrentLeftRight.X,Options.CurrentLeftRight.Y,Options.CurrentBottomTop.X,Options.CurrentBottomTop.Y,Options.CurrentNear,Options.CurrentFar);
						break;
					}
				case CM_3D:
					{
						f32 Aspect = (f32)ViewportSize.X / (f32)ViewportSize.Y;
						ProjMat.BuildPerspectiveMatrix(Options.CurrentFov,Aspect,Options.CurrentNear,Options.CurrentFar);
						break;
					}
				}
				NeedToRebuildProjMat = false;
			}

			if(NeedToRebuildViewMat)
			{
				ViewMat.BuildLookAtMatrix(AbsoluteModelMatrix.GetTranslation(),AbsoluteModelMatrix.MultiplyVectorOnMatrix(Options.CurrentLookAt).Normalize()+AbsoluteModelMatrix.GetTranslation(),AbsoluteModelMatrix.MultiplyVectorOnMatrix(Options.CurrentUp).Normalize());
				Frustum->CalculateFrustum(ProjMat,ViewMat);

				NeedToRebuildViewMat = false;
			}

			CurrentRender->SetViewport(Viewport.CurrentFullSizeViewport,Viewport.CurrentPosition.X,Viewport.CurrentPosition.Y,Viewport.CurrentSize.X,Viewport.CurrentSize.Y);
			CurrentRender->SetMatrix(ProjMat,video::RMT_PROJECTION);
			CurrentRender->SetMatrix(ViewMat,video::RMT_VIEW);
		}

		//! Get Frustum
		jeFrustum * CCameraSceneNode::GetFrustum()
		{
			return Frustum;
		}

		//! Set Camera Options
		void CCameraSceneNode::SetCameraOptions(const CameraOptions & NewCameraOptions)
		{
			Options = NewCameraOptions;
			NeedToRebuildProjMat = true;
		}

		//! Get Camera Options
		const ICameraSceneNode::CameraOptions & CCameraSceneNode::GetCameraOptions()
		{
			return Options;
		}

		//! Set Viewport Options
		void CCameraSceneNode::SetViewportOptions(const ViewportOptions & NewViewportOptions)
		{
			Viewport = NewViewportOptions;

			if(Options.CurrentMode == CM_2D)
				NeedToRebuildProjMat = true;
		}

		//! Get Viewport Options
		const ICameraSceneNode::ViewportOptions & CCameraSceneNode::GetViewportOptions()
		{
			return Viewport;
		}

		//! Rebuild Transformations
		void CCameraSceneNode::RebuildTransformations()
		{
			if(NeedToRebuildMatrix)
				NeedToRebuildViewMat = true;
			ISceneNode::RebuildTransformations();
		}

		//! Build Absolute Model Matrix
		void CCameraSceneNode::BuildAbsoluteModelMatrix(const jeMatrix4f & ParentMatrix,const jeVector3df & ParentScale,u1 Changed)
		{
			if(Changed)
				NeedToRebuildViewMat = true;

			ISceneNode::BuildAbsoluteModelMatrix(ParentMatrix,ParentScale,Changed);
		}

		//! Serialize
		void CCameraSceneNode::Serialize(core::ISerializeStream * SerializeStream)
		{
			JE_SS_BEGIN(CameraSceneNode)
			JE_SS_SIZE(Options,sizeof(CameraOptions))
			JE_SS_SIZE(Viewport,sizeof(ViewportOptions))
			JE_SS_END(CameraSceneNode)

			if(JE_SS_LOAD)
			{
				NeedToRebuildProjMat = true;
				NeedToRebuildViewMat = true;
			}

			ISceneNode::Serialize(SerializeStream);
		}

	}
}
